// 导入Java脚本引擎包，用于计算表达式
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
// 导入Swing GUI组件包
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
// 导入AWT布局和事件处理包
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
// 导入音频处理包
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
// 导入日期时间处理包
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// 导入定时器包用于实时更新时间
import java.util.Timer;
import java.util.TimerTask;

/**
 * 高级计算器应用程序
 * 功能包括基本算术运算、科学计算、实时时间显示和背景音乐播放
 * 界面采用现代UI设计，支持按钮悬停效果和多种视觉反馈
 */
public class Calculator extends JFrame implements ActionListener {

    // 计算器应用程序的主要组件
    private JPanel north, center;         // 北部面板(显示区)和中心面板(按钮区)
    private JTextField field;             // 显示计算表达式和结果的文本框
    private JButton[] btns;               // 存储所有按钮的数组

    // 按钮颜色配置 - 使用自定义颜色提升UI美观度
    private static final Color NUM_COLOR = new Color(200, 230, 255);      // 数字按钮背景色(浅蓝色)
    private static final Color OPERATOR_COLOR = new Color(230, 200, 255);  // 运算符按钮背景色(浅紫色)
    private static final Color FUNCTION_COLOR = new Color(255, 200, 230);  // 功能按钮背景色(浅粉色)
    private static final Color HOVER_COLOR = new Color(180, 180, 255);     // 按钮悬停时的背景色(中蓝色)
    private static final Color PRESSED_COLOR = new Color(150, 150, 255);   // 按钮按下时的背景色(深紫色)

    // 按钮边框配置 - 使用细灰色边框增强按钮视觉效果
    private static final Border BUTTON_BORDER = new LineBorder(new Color(150, 150, 150), 1);

    // 音频相关变量
    private Clip backgroundMusic; // 背景音乐播放器
    private static final String MUSIC_PATH = "D:\\jianxi\\music.wav";
    
    // 连续按两次C键的计时器和计数器
    private long lastCTime = 0;     // 上次按下C键的时间
    private int cPressCount = 0;    // C键按下次数计数器
    
    // 新增：实时时间更新相关变量
    private Timer timer;            // 定时器
    private boolean isTimeMode = false; // 是否处于时间显示模式

    /**
     * 计算器构造函数
     * 初始化窗口属性并设置界面组件
     */
    public Calculator() {
        // 设置窗口title
        setTitle("计算器");
        // 设置窗口大小
        setSize(700, 700);
        // 设置窗口关闭时程序
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        // 设置窗口居中显示
        setLocationRelativeTo(null);
        // 初始化界面组件
        initComponent();
        // 初始化并播放背景音乐
        initAndPlayMusic();
        // 设置窗口显示
        setVisible(true);
        // 启动实时时间更新
        startRealTimeUpdate();
    }

    /**
     * 初始化界面组件
     * 创建并配置计算器的所有UI元素
     */
    private void initComponent() {
        // System.out.println("initComponent");
        // 创建面板容器
        // 容器面板 文本框区域
        north = new JPanel();
        // 设置北部面板背景颜色为浅灰色
        north.setBackground(new Color(230, 230, 230));
        // 容器面板 按钮区域
        center = new JPanel();
        // 设置中部面板背景颜色为浅灰色
        center.setBackground(new Color(230, 230, 230));
        // 文本框 显示计算过程、结果或者日期时间
        field = new JTextField();
        // 设置输入框大小 首选尺寸
        field.setPreferredSize(new Dimension(700, 150));
        // 禁用文字输入 禁止通过电脑键盘输入编辑
        field.setEditable(false);
        // 设置输入框中文字大小和颜色
        field.setFont(new Font("SansSerif", Font.BOLD, 35));
        field.setForeground(Color.BLACK);
        // 设置文本框背景颜色为白色
        field.setBackground(Color.WHITE);

        // 按钮文本内容 - 增加了倒数(1/x)、平方根(√)和三次方(x³)按钮
        String[] str = new String[] {
                "7", "8", "9", "+",
                "4", "5", "6", "-",
                "1", "2", "3", "×",
                "0", ".", "=", "÷",
                "C", "B", "T", "S",
                "1/x", "√", "x³", "%"
        };

        // 按钮数组
        btns = new JButton[str.length];
        for (int i = 0; i < btns.length; i++) {
            btns[i] = new JButton(str[i]);
            btns[i].setFont(new Font("SansSerif", Font.BOLD, 25));
            // 设置按钮的边框
            btns[i].setBorder(BUTTON_BORDER);
            // 设置按钮的背景颜色和文本颜色
            // 数字按钮为浅蓝色
            if (i >= 0 && i <= 11 && i % 4 != 3) {
                btns[i].setBackground(NUM_COLOR);
            }
            // 运算符按钮为浅紫色
            else if (i % 4 == 3 && i < 15) {
                btns[i].setBackground(OPERATOR_COLOR);
            }
            // 功能按钮为浅粉色
            else {
                btns[i].setBackground(FUNCTION_COLOR);
            }
            btns[i].setForeground(Color.BLACK);
            btns[i].setFocusPainted(false); // 去除焦点边框

            // 添加鼠标悬停效果
            final int index = i; // 保存当前按钮的索引，以便在内部类中访问
            btns[i].addMouseListener(new MouseAdapter() {
                @Override
                public void mouseEntered(MouseEvent e) {
                    // 当鼠标进入按钮区域时触发
                    // 将按钮背景色设置为统一的悬停颜色(HOVER_COLOR)
                    // 悬停时颜色变深
                    btns[index].setBackground(HOVER_COLOR);
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    // 当鼠标离开按钮区域时触发
                    // 根据按钮类型恢复原始背景色
                    if (index >= 0 && index <= 11 && index % 4 != 3) {
                        // 索引0-11且非第4列的按钮为数字按钮
                        btns[index].setBackground(NUM_COLOR);
                    } else if (index % 4 == 3 && index < 15) {
                        // 第4列(索引3,7,11,15)的按钮为运算符按钮
                        btns[index].setBackground(OPERATOR_COLOR);
                    } else {    // 其余按钮为功能按钮
                        btns[index].setBackground(FUNCTION_COLOR);
                    }
                }

                @Override
                public void mousePressed(MouseEvent e) {
                    // 当鼠标按下按钮时触发
                    // 将按钮背景色设置为按下状态颜色(PRESSED_COLOR)
                    // 按下时颜色更深
                    btns[index].setBackground(PRESSED_COLOR);
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    // 当鼠标释放按钮时触发
                    // 将按钮背景色恢复为悬停颜色(HOVER_COLOR)
                    // 此时鼠标可能仍在按钮上，因此保持悬停效果
                    btns[index].setBackground(HOVER_COLOR);
                }
            });
        }

        // 将组件放到窗口中
        // 设置布局管理器
        // north 顶部面板 使用 BorderLayout 边界布局
        north.setLayout(new BorderLayout());
        // center 中间面板 使用6行4列 的 网格布局（增加一行）
        center.setLayout(new GridLayout(6, 4, 5, 5)); // 添加按钮间距

        // 组件设置
        // field 文本框 添加到 north 顶部面板 使用 BorderLayout 居中
        north.add(field, BorderLayout.CENTER);
        // btns 按钮 添加到 center 面板 增强for循环
        for (JButton btn : btns) {
            center.add(btn);
        }

        // 将 north 顶部面板 添加到窗口中
        add(north, BorderLayout.NORTH);
        // 将 center 面板 添加到窗口中
        add(center);

        // 绑定按钮的监听事件
        for (JButton btn : btns) {
            btn.addActionListener(this);
        }
    }

    /**
     * 处理按键文本
     * 根据用户点击的按钮执行相应操作
     * @param text 按钮上的文本
     */
    private void setBtnText(String text) {
        // System.out.println(" text length:" + text.length());
        // System.out.println(" field text length:" + field.getText().length());
        String fieldText = field.getText();
        
        // 连续按两次C键的逻辑
        if ("C".equals(text)) {
            long currentTime = System.currentTimeMillis();
            
            // 检查是否在500毫秒内连续点击两次C键
            if (currentTime - lastCTime < 500) {
                cPressCount++;
                
                // 第二次点击C键，暂停或继续播放音乐
                if (cPressCount == 2) {
                    toggleBackgroundMusic();
                    cPressCount = 0;  // 重置计数器
                    return;  // 直接返回，不执行常规的C键清除操作
                }
            } else {
                // 超过时间间隔，重置计数器
                cPressCount = 1;
            }
            
            lastCTime = currentTime;  // 更新最后点击时间
        } else {
            // 点击其他按钮，重置C键计数器
            cPressCount = 0;
        }
        
        // 新增：处理时间模式切换
        if ("T".equals(text)) {
            isTimeMode = !isTimeMode;  // 切换时间显示模式
            
            if (isTimeMode) {
                // 进入时间模式时立即更新一次时间
                updateTimeDisplay();
                field.setText("实时时间已开启");
                try {
                    Thread.sleep(500);  // 短暂延迟，让用户看到提示
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                // 退出时间模式时清空显示或恢复之前的内容
                if (fieldText.startsWith("当前时间: ")) {
                    field.setText("");
                }
            }
            return;
        }
        
        // 如果处于时间模式且按下非时间按钮，退出时间模式
        if (isTimeMode && !"T".equals(text)) {
            isTimeMode = false;
        }
        
        // 原有的按钮处理逻辑
        switch (text) {
            case "C":                // 清除按钮
                // 功能：清空文本框内容，重置计算器状态
                field.setText("");
                System.out.println("C");   // 当前仅打印日志，实际应实现清除功能
                playSystemSound(); // 播放系统提示音
                break;
            case "B":                // 退格按钮
                // 功能：删除文本框中最后一个字符
                // 1. 获取输入框的内容
                String numText = field.getText();
                // 两种情况 可以通过isEmpty()或者.length()等方法判断
                if(numText.length() != 0){
                    // numText的长度-1 789,
                    String lastText = numText.substring(0,numText.length()-1);
                    System.out.println("lastText:" + lastText);
                    //将截取的文本设置到输入框
                    field.setText(lastText);
                } else{
                }
                System.out.println("B");   // 当前仅打印日志，实际应实现退格功能
                playSystemSound(); // 播放系统提示音
                break;
            case "T":                // 时间按钮 - 已在上方处理
                break;
            case "S":                // 平方计算按钮
                // 功能：计算当前数字的平方
                calculateSquare();
                playSystemSound(); // 播放系统提示音
                break;
            case "1/x":              // 倒数计算按钮
                // 功能：计算当前数字的倒数(1/x)
                calculateReciprocal();
                playSystemSound(); // 播放系统提示音
                break;
            case "√":                // 平方根计算按钮
                // 功能：计算当前数字的平方根
                calculateSquareRoot();
                playSystemSound(); // 播放系统提示音
                break;
            case "x³":               // 三次方计算按钮
                // 功能：计算当前数字的三次方
                calculateCube();
                playSystemSound(); // 播放系统提示音
                break;
            case "%":                // 百分比计算按钮
                // 功能：将当前数字转换为百分比形式
                calculatePercentage();
                playSystemSound(); // 播放系统提示音
                break;
            case "=":                // 等于按钮
                calculate();
                // 功能：计算表达式结果并显示
                // System.out.println("=:执行计算功能");   // 当前仅打印日志，实际应实现计算功能
                playSystemSound(); // 播放系统提示音
                break;
            case "+":
            case "-":
            case "×":
            case "÷":
                // 操作符处理逻辑
                if (fieldText.isEmpty()) {
                    // 操作符不能出现在开头，除非是减号（表示负数）
                    if (!"-".equals(text)) {
                        System.out.println("操作符不能出现在数字前面");
                        showErrorDialog("操作错误", "操作符不能出现在数字前面");
                        playSystemSound(); // 播放系统提示音
                        break;
                    }
                }
                
                // 特殊处理减号：允许作为开头，但不能连续出现
                if ("-".equals(text)) {
                    if (fieldText.isEmpty()) {
                        // 允许减号作为开头
                        field.setText("-");
                    } else if (fieldText.endsWith("-")) {
                        // 不允许连续的减号（避免 --5 或 5--6 等情况）
                        System.out.println("不允许连续的减号");
                        showErrorDialog("操作错误", "不允许连续的减号");
                        playSystemSound();
                        break;
                    } else {
                        // 正常追加减号（如 5- 或 -5+3 ）
                        field.setText(fieldText + "-");
                    }
                } else {
                    // 其他操作符处理逻辑（+、×、÷）
                    if (fieldText.endsWith("+") || fieldText.endsWith("×") || 
                        fieldText.endsWith("÷") || 
                        (fieldText.endsWith("-") && fieldText.length() > 0)) {
                        // 替换最后一个操作符（避免连续操作符）
                        fieldText = fieldText.substring(0, fieldText.length() - 1) + text;
                        field.setText(fieldText);
                    } else {
                        // 正常追加操作符
                        field.setText(fieldText + text);
                    }
                }
                playSystemSound(); // 播放系统提示音
                break;
            case ".":
                // 小数点处理逻辑：前面没有数字时自动补充为0
                if (fieldText.isEmpty()) {
                    // 空输入框直接输入小数点，补0
                    field.setText("0.");
                } else {
                    boolean endsWithOperator = fieldText.endsWith("+") || fieldText.endsWith("-") ||
                            fieldText.endsWith("×") || fieldText.endsWith("÷");

                    // 获取当前数字部分（最后一个操作符之后的内容）
                    String currentNumber = fieldText;
                    int lastOpIndex = Math.max(
                        Math.max(fieldText.lastIndexOf("+"), fieldText.lastIndexOf("-")),
                        Math.max(fieldText.lastIndexOf("×"), fieldText.lastIndexOf("÷"))
                    );
                    if (lastOpIndex >= 0) {
                        currentNumber = fieldText.substring(lastOpIndex + 1);
                    }

                    if (currentNumber.contains(".")) {
                        // 当前数字已有小数点，不允许重复输入
                        System.out.println("小数点不能连续出现");
                        showErrorDialog("输入错误", "小数点不能连续出现");
                    } else if (endsWithOperator) {
                        // 操作符后输入小数点，补0
                        field.setText(fieldText + "0.");
                    } else {
                        // 正常追加小数点
                        field.setText(fieldText + text);
                    }
                }
                playSystemSound(); // 播放系统提示音
                break;
            default:                 // 数字按钮
                // 功能：将按钮文本添加到文本框中
                String oldText = field.getText(); // 获取当前文本框内容
                field.setText(oldText + text);   // 追加新的按钮文本
                playSystemSound(); // 播放系统提示音
                break;
        }
    }

    /**
     * 计算平方功能
     * 将当前文本框中的数字进行平方运算并显示结果
     */
    private void calculateSquare() {
        String text = field.getText();
        try {
            // 尝试将输入转换为数字
            double num = Double.parseDouble(text);
            // 计算平方
            double square = num * num;
            // 显示结果
            field.setText(num + "² = " + square);
        } catch (NumberFormatException e) {
            // 输入不是数字时提示错误
            showErrorDialog("计算错误", "请先输入有效的数字");
            System.out.println("平方计算错误: 输入不是有效数字");
        }
    }

    /**
     * 计算倒数功能
     * 将当前文本框中的数字进行倒数运算并显示结果
     */
    private void calculateReciprocal() {
        String text = field.getText();
        try {
            double num = Double.parseDouble(text);
            if (num == 0) {
                // 强化0作为除数的提示
                showErrorDialog("数学错误", "错误：0没有倒数，不能计算1/0");
                return;
            }
            double reciprocal = 1 / num;
            field.setText("1/" + num + " = " + reciprocal);
        } catch (NumberFormatException e) {
            showErrorDialog("计算错误", "请先输入有效的数字");
            System.out.println("倒数计算错误: 输入不是有效数字");
        }
    }

    /**
     * 计算平方根功能
     * 1. 从文本框获取用户输入的数值
     * 2. 验证输入是否为合法数字
     * 3. 检查数值是否为非负数
     * 4. 计算平方根并格式化显示结果
     */
    private void calculateSquareRoot() {
        // 获取当前文本框中的输入内容
        String text = field.getText();
        
        try {
            // 将输入文本转换为双精度浮点数
            double num = Double.parseDouble(text);
            
            // 检查数值是否为负数，负数没有实数平方根
            if (num < 0) {
                // 显示错误信息
                showErrorDialog("计算错误", "错误：负数没有平方根");
                // 终止方法执行
                return;
            }
            
            // 调用Math库的sqrt方法计算平方根
            double sqrt = Math.sqrt(num);
            
            // 格式化并显示计算结果
            // 示例输出: √25 = 5.0
            field.setText("√" + num + " = " + sqrt);
            
        } catch (NumberFormatException e) {
            // 处理输入不是有效数字的情况
            showErrorDialog("计算错误", "请先输入有效的数字");
            // 记录错误日志（控制台输出）
            System.out.println("平方根计算错误: 输入不是有效数字");
        }
    }

    /**
     * 计算三次方功能
     * 将当前文本框中的数字进行三次方运算并显示结果
     */
    private void calculateCube() {
        String text = field.getText();
        try {
            double num = Double.parseDouble(text);
            double cube = num * num * num;
            field.setText(num + "³ = " + cube);
        } catch (NumberFormatException e) {
            showErrorDialog("计算错误", "请先输入有效的数字");
            System.out.println("三次方计算错误: 输入不是有效数字");
        }
    }

    /**
     * 计算百分比功能
     * 将当前文本框中的数字转换为百分比形式并显示结果
     */
    private void calculatePercentage() {
        String text = field.getText();
        try {
            double num = Double.parseDouble(text);
            double percentage = num / 100;
            field.setText(num + "% = " + percentage);
        } catch (NumberFormatException e) {
            showErrorDialog("计算错误", "请先输入有效的数字");
            System.out.println("百分比计算错误: 输入不是有效数字");
        }
    }

    /**
     * 执行计算功能
     * 使用JavaScript引擎计算表达式结果并显示
     */
    private void calculate() {
        // 用户输入 1+1 我们执行计算操作 然后将结果展示到文本框
        // 用户输入 1 + 1
        // 如果表达式当中有 × ÷ 要替换为 * /
        String expression = field.getText();
        
        // 检查是否存在除以0的情况
        if (expression.contains("÷0") || expression.contains("/0")) {
            // 检查是否是类似 5÷0.5 这样的合法表达式（0后面有小数点）
            boolean isValid = false;
            int indexDiv = expression.indexOf("÷0");
            int indexSlash = expression.indexOf("/0");
            
            if (indexDiv != -1) {
                if (indexDiv + 2 < expression.length() && expression.charAt(indexDiv + 2) == '.') {
                    isValid = true;
                }
            }
            
            if (indexSlash != -1) {
                if (indexSlash + 2 < expression.length() && expression.charAt(indexSlash + 2) == '.') {
                    isValid = true;
                }
            }
            
            if (!isValid) {
                showErrorDialog("数学错误", "错误：除数不能为0");
                return;
            }
        }
        
        //替换中文运算符为英文运算符
        expression = expression.replace("×", "*").replace("÷", "/");
        
        //使用js计算引擎
        ScriptEngineManager manager = new ScriptEngineManager();
        // 初始化计算引擎 使用javascript解析数学表达式
        ScriptEngine engine = manager.getEngineByName("javascript");
        
        //开始计算
        try{  // try 中放的是可能出现异常的代码
            // 开始计算 计算过程可能出现异常 这里需要处理异常
            Object result = engine.eval(expression);
            // 然后将结果展示到文本框
            field.setText(result.toString());
        } catch (ScriptException e) {
            // catch 中放的是异常的处理方式
            showErrorDialog("运算错误", "表达式格式不正确，请检查后重试");
            e.printStackTrace();
        }
    }

    /**
     * 显示错误对话框
     * @param title 对话框标题
     * @param message 错误消息内容
     */
    private void showErrorDialog(String title, String message) {
        // 播放错误提示音
        playSystemSound();
        
        // 创建错误对话框
        JOptionPane.showMessageDialog(
            this,                  // 父组件，对话框将居中显示在该组件上
            message,               // 显示的消息内容
            title,                 // 对话框标题
            JOptionPane.ERROR_MESSAGE  // 错误消息类型，显示错误图标
        );
    }

    /**
     * 播放系统提示音（无需外部文件）
     * 使用Java AWT工具包播放默认提示音
     */
    private void playSystemSound() {
        // 使用Toolkit.getDefaultToolkit().beep()播放系统默认提示音
        java.awt.Toolkit.getDefaultToolkit().beep();
    }

    /**
     * 初始化并播放背景音乐
     * 从指定路径加载WAV文件并循环播放
     */
    private void initAndPlayMusic() {
        try {
            // 加载音频文件（使用你提供的路径）
            File musicFile = new File(MUSIC_PATH);
            if (musicFile.exists()) {
                // 获取音频剪辑
                backgroundMusic = AudioSystem.getClip();
                backgroundMusic.open(AudioSystem.getAudioInputStream(musicFile));

                // 循环播放音乐（-1表示无限循环）
                backgroundMusic.loop(Clip.LOOP_CONTINUOUSLY);
                System.out.println("背景音乐已启动，路径：" + MUSIC_PATH);
            } else {
                System.out.println("背景音乐文件不存在: " + MUSIC_PATH);
            }
        } catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
            System.out.println("播放背景音乐时出错: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * 切换背景音乐播放状态（暂停/继续）
     * 控制背景音乐的播放与暂停
     */
    private void toggleBackgroundMusic() {
        if (backgroundMusic == null) {
            return;
        }
        
        if (backgroundMusic.isRunning()) {
            backgroundMusic.stop();  // 暂停音乐
            field.setText("音乐已暂停");
            System.out.println("背景音乐已暂停");
        } else {
            backgroundMusic.start();  // 继续播放
            field.setText("音乐已恢复");
            System.out.println("背景音乐已恢复");
        }
    }

    /**
     * 窗口关闭时释放资源
     * 停止定时器并关闭背景音乐
     */
    @Override
    public void dispose() {
        if (timer != null) {
            timer.cancel();  // 停止定时器
        }
        
        if (backgroundMusic != null && backgroundMusic.isOpen()) {
            backgroundMusic.stop();
            backgroundMusic.close();
            System.out.println("背景音乐已停止");
        }
        super.dispose();
    }

    /**
     * 实现 ActionListener 接口中的抽象方法
     * 处理按钮点击事件
     * @param a 动作事件对象
     */
    @Override
    public void actionPerformed(ActionEvent a) {
        // 此处可以捕获到 ActionListener 监听的事件
        // System.out.println(a.getSource());
        // 获取被点击的按钮 或者按钮的文字
        Object source = a.getSource();
        if (source instanceof JButton) {
            JButton btn = (JButton) source;
            String text = btn.getText();
            // 处理对应按键文字
            setBtnText(text);
        }
    }

    /**
     * 启动实时时间更新
     * 创建定时器，每秒更新一次时间显示
     */
    private void startRealTimeUpdate() {
        timer = new Timer(true);  // 创建守护线程的定时器
        
        // 每秒执行一次时间更新任务
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                // 在EDT线程中更新UI
                SwingUtilities.invokeLater(() -> {
                    if (isTimeMode) {
                        updateTimeDisplay();
                    }
                });
            }
        }, 0, 1000);  // 立即开始，每秒执行一次
    }
    
    /**
     * 更新时间显示
     * 获取当前系统时间并格式化为"YYYY-MM-DD HH:MM:SS"格式显示
     */
    private void updateTimeDisplay() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        field.setText("当前时间: " + now.format(formatter));
    }

    /**
     * 程序入口点
     * 在事件调度线程中创建并显示计算器窗口
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 是 Java Swing 开发中确保线程安全的核心代码
        // EDT 线程保障
        // Swing 要求所有 GUI 操作（组件创建、属性修改、事件处理）必须在 Event Dispatch Thread (EDT) 执行。这里代码通过
        // invokeLater 将 Calculator 类的实例化操作（即 GUI 初始化）提交到 EDT 队列，确保线程安全。
        // 异步执行机制
        // invokeLater 会将传入的 Runnable 任务（此处为 Calculator::new 方法引用）放入 EDT 的任务队列，
        SwingUtilities.invokeLater(Calculator::new);
    }
}